package com.propertree.mailer.publisher;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Callable;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;

import com.google.common.collect.Lists;
import com.propertree.mailer.bean.EMailPartyInfo;
import com.propertree.mailer.bean.EmailAttributes;
import com.propertree.mailer.bean.EmailAttributesConfiguration;
import com.propertree.mailer.bean.MailPublisherConfiguration;
import com.propertree.mailer.bean.SenderReceiverConfig;
import com.propertree.mailer.config.EmailAttributeHelper;
import com.propertree.mailer.config.EmailServiceProviderConfiguration;
import com.propertree.mailer.config.MailerConfigContainer;
import com.propertree.mailer.helper.MessageDecorator;
import com.propertree.mailer.helper.SessionHelper;
import com.propertree.mailer.manager.ExcelSenderReceiverInfo;
import com.propertree.mailer.manager.MailerSenderReceiverInfo;

public class MailPublisher implements Callable<Boolean>{
	private static final String LINE_SEPARATOR = System.getProperty("line.separator");

	@Override
	public Boolean call() throws Exception {
		try {
			execute();
		} catch (Exception e) {
			return Boolean.FALSE;
		}
		return Boolean.TRUE;
	}
	
	private void  execute() {
		Properties props = EmailServiceProviderConfiguration.createServiceProviderProperties();
		MailerConfigContainer mailerConfigContainer = MailerConfigContainer.getInstance();

		// Below block will be based on UI config.
		SenderReceiverConfig config = mailerConfigContainer.getSenderReceiverConfig();
		String senderFilePath = mailerConfigContainer.getSenderReceiverConfig().getSenderFilePath();// System.getProperty("senderFilePath");
		validateFilePath(senderFilePath, "xls");
		config.setSenderFilePath(senderFilePath); // "D:\\Hitesh\\ProperTree\\config\\sender.xls"

		String receiverFilePath = mailerConfigContainer.getSenderReceiverConfig().getReceiverFilePath();// System.getProperty("receiverFilePath");
		validateFilePath(receiverFilePath, "xls");
		config.setReceiverFilePath(receiverFilePath); // "D:\\Hitesh\\ProperTree\\config\\receiver.xls"

		MailerSenderReceiverInfo info = new ExcelSenderReceiverInfo();
		List<EMailPartyInfo> senders = info.getSendersInfo(config);
		List<EMailPartyInfo> receiversInfo = info.getReceiversInfo(config);
		List<String> outputData = new ArrayList<String>(receiversInfo.size());

		// Below block will be based on UI config
		EmailAttributesConfiguration emailAttributesConfiguration = mailerConfigContainer.getEmailAttribConfig();
		String mailSubFilePath = mailerConfigContainer.getEmailAttribConfig().getEmailSubjectFile();// System.getProperty("mailSubFilePath");
		validateFilePath(mailSubFilePath, "txt");
		emailAttributesConfiguration.setEmailSubjectFile(mailSubFilePath);
		String mailBodyFilePath = mailerConfigContainer.getEmailAttribConfig().getEmailBodyFile();// System.getProperty("mailBodyFilePath");
		validateFilePath(mailSubFilePath, "txt");
		emailAttributesConfiguration.setEmailBodyFile(mailBodyFilePath);
		EmailAttributes emailAttributes = EmailAttributeHelper.createEmailAttribute(emailAttributesConfiguration);

		// Below block will be based on UI config
		String outputFile = mailerConfigContainer.getMailPubConfig().getOutputFilePath();// System.getProperty("outputFilePath");
		validateFilePath(outputFile, "txt");

		MailPublisherConfiguration publisherConfiguration = mailerConfigContainer.getMailPubConfig();
		int blockSizeStr = mailerConfigContainer.getMailPubConfig().getBlockSize();// System.getProperty("blockSize",
																					// "5");
		publisherConfiguration.setBlockSize(blockSizeStr);

		long pausePerMessageInMillis = mailerConfigContainer.getMailPubConfig().getPausePerMessageInMillis();// System.getProperty("pausePerMessageInMillis",
																												// "2000");
		publisherConfiguration.setPausePerMessageInMillis(pausePerMessageInMillis);

		long pausePerBlockInMiilis = mailerConfigContainer.getMailPubConfig().getPausePerBlockInMiilis();// System.getProperty("pausePerBlockInMiilis",
																											// "5000");
		publisherConfiguration.setPausePerBlockInMiilis(pausePerBlockInMiilis);

		String senderReceiverTuple = "";
		try {
			List<List<EMailPartyInfo>> receiverPartyList = getPartyBlock(receiversInfo,
					publisherConfiguration.getBlockSize());
			int receiverSize = receiverPartyList.size();
			int counter = 0;
			// int blockSize = publisherConfiguration.getBlockSize();

			for (EMailPartyInfo senderParty : senders) {
				List<EMailPartyInfo> receivers = null;
				if (counter < receiverSize) {
					receivers = receiverPartyList.get(counter++);
				}
				if (receivers == null) {
					break;
				}
				Session session = SessionHelper.createSession(props, senderParty);
				for (EMailPartyInfo receiverParty : receivers) {
					senderReceiverTuple = "From :" + senderParty.getEmailId() + " To :" + receiverParty.getEmailId();
					Message message = MessageDecorator.createMessage(session, senderParty, receiverParty,
							emailAttributes);
					Transport.send(message);
					outputData.add("Email Sent. From ::" + senderParty.getEmailId() + " | To :: "
							+ receiverParty.getEmailId() + LINE_SEPARATOR);
					Thread.sleep(publisherConfiguration.getPausePerMessageInMillis());
				}

				session = null;
				Thread.sleep(publisherConfiguration.getPausePerBlockInMiilis());
			}
			// System.out.println(outputData);
		} catch (MessagingException e) {
			outputData.add("Error :: " + senderReceiverTuple);
		} catch (Exception e) {
			outputData.add("Error :: " + senderReceiverTuple);
		} finally {
			try {
				BufferedWriter bf = new BufferedWriter(new FileWriter(new File(outputFile + "_"
						+ System.currentTimeMillis())));
				for (String data : outputData) {
					bf.write(data);
					bf.flush();
				}
				bf.close();
			} catch (IOException e) {
				System.out.println("Error while writing outputfile. Below emails have been sent.");
				System.out.println(outputData);
			}
		}
	}

	private static List<List<EMailPartyInfo>> getPartyBlock(List<EMailPartyInfo> receivers, int blockSize) {
		return Lists.partition(receivers, blockSize);
	}

	private static void validateFilePath(String filePath, String ext) {
		if (filePath == null || filePath.trim().equals("")) {
			System.out.println("One more more file paths are not given or not legal. Please check the path properly.");
			System.exit(-1);
		}

		File file = new File(filePath);
		if (!file.exists()) {
			System.out.println("File :: " + filePath + " doesn't exist. Please check the path properly.");
			System.exit(-1);
		}

		if (!file.canRead()) {
			System.out.println("File :: " + filePath + " can't be read. Please check file permission.");
			System.exit(-1);
		}

		if (!file.isFile()) {
			System.out.println("File :: " + filePath + " seems a directory. Please check properly.");
			System.exit(-1);
		}
		String fileName = file.getName();
		int dot = fileName.lastIndexOf('.');
		if (dot <= 0) {
			System.out.println("File :: " + filePath
					+ " doesn't seem to have proper extension. Check it is 'xls' file.");
			System.exit(-1);
		}
		String extension = (dot == -1) ? "" : fileName.substring(dot + 1);

		if (!ext.equalsIgnoreCase(extension)) {
			System.out.println("File :: " + filePath + " doesn't seem to have proper extension. Check it is '" + ext
					+ "' file.");
			System.exit(-1);
		}
	}

	private static int getValidField(String propertyName, String blockSizeStr) {
		if (blockSizeStr == null || blockSizeStr.trim().equals("")) {
			System.out.println(propertyName + " is not proper.");
			System.exit(-1);
		}
		Integer blockSize = 0;
		try {
			blockSize = Integer.parseInt(blockSizeStr);

		} catch (Exception e) {
			System.out.println(propertyName + " is not proper.");
			System.exit(-1);
		}

		return blockSize;
	}
}
